package core;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import sun.java2d.loops.DrawGlyphListAA.General;

public class Genetic extends Thread {   
    
    public static Random random = new Random();
    public static int bestFitness;
    public static int[] bestIndividu;
    
    private FactoryPanel fp;
    
    @Override
    public void run() {        
        System.out.println("Mulai");
        int generation=0;
        int maxGeneration = fp.getMaxGeneration();                
        
        Output.getInstance().println("**********PEMBANGKITAN POPULASI************");
        ArrayList<int[]> individuList = Genetic.CreateIndividuals(fp.getInitialPopulationNumber(), Factory.getInstance().WORK_HOUR, Factory.getInstance().getAllRentableMachines());                        
        generation++;//populasi awal adalah generasi pertama                
        
        bestIndividu = cloneIndividu(getFittestIndividu(individuList));
        bestFitness = FitnessSimulate(bestIndividu,7);
        
        System.out.println("a : " + bestFitness);
        for(int a : bestIndividu) {
            System.out.print(a + " ");
        }
        System.out.println("");
        
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                fp.setGeneration(1);
            }                        
        });
        
        while(FactoryPanel.run && (generation<=maxGeneration)) {                                              
            final int temp = generation;
            Output.getInstance().println("**********PENYELEKSIAN POPULASI************");
            individuList = Genetic.SelectIndividu(individuList, Factory.getInstance().getAllRentableMachines());
            Output.getInstance().println("***************CROSS OVER******************");
            individuList = Genetic.CrossoverMutateIndividuList(individuList);               
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    fp.setGeneration(temp);
                }                        
            });
            generation++;
        }
        Output.getInstance().println("genetic selesai");        
        Output.getInstance().print("Individu paling potensial : ");                
        Output.getInstance().print(bestIndividu);              
        try {
            SwingUtilities.invokeAndWait(new Runnable() {
                @Override
                public void run() {
                    fp.setFittestIndividu(bestIndividu);                                    
                    fp.setGARun(false);
                }
            });
        } catch (InterruptedException ex) {
            Logger.getLogger(Genetic.class.getName()).log(Level.SEVERE, null, ex);
        } catch (InvocationTargetException ex) {
            Logger.getLogger(Genetic.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public Genetic(FactoryPanel fp) {
        this.fp = fp;
    }
    
    public static int[] createRandomIndividu(int factoryWorkHour, Machine[] machinesAvailable) {        
        int money = Factory.getInstance().money;
        int machineVariantCount = 0;
        ArrayList<Machine> avail = new ArrayList<Machine>(Arrays.asList(machinesAvailable)); // daftar kandidat mesin yang masi mungkin dipilih       
        Machine[] independentMachines = searchIndependentMachine(machinesAvailable);               
        ArrayList<MachineWorkHourPair> availableSelectedMachinesPair = new ArrayList<MachineWorkHourPair>(); // jenis mesin yang sudah pernah dipilih sebelumnya dan masih bisa digunakan(jam kerja-nya masih dibawah maksimum)                   
        java.util.Map<Integer, Integer> productMap = new HashMap<Integer,Integer>();
        //ArrayList<MachineWorkHourPair> selectedMachinesPair = new ArrayList<MachineWorkHourPair>(); //untuk keperluan loggin        
        Machine candidate;        
        ArrayList<Integer> state = new ArrayList<Integer>(factoryWorkHour);
        
        while(state.size() < Factory.getInstance().WORK_HOUR) {                                    
            if(state.isEmpty()) {                
                //bangkitin mesin yang gak butuh bahan apa2 jika mengisi jam pertama                
                candidate = independentMachines[random.nextInt(independentMachines.length)];                
            }else {  
                do {                    
                    if(machineVariantCount<Factory.getInstance().MAX_RENTABLE_MACHINES_BY_FACTORY) {
                        // jika masih bisa nambah jenis mesin baru                           
                        candidate = avail.get(random.nextInt(avail.size()));
                    }else if(availableSelectedMachinesPair.size()>0){
                        //jika tidak bisa nambah jenis mesin baru, pergunakan mesin yang sudah dipilih                                        
                        candidate = availableSelectedMachinesPair.get(random.nextInt(availableSelectedMachinesPair.size())).machine;
                    }else{
                        //jika tidak ada kandidat mesin yang dapat dipilih lagi, kosongkan
                        candidate = null;
                        break;
                    }  
                    
                    if(candidate.DEPENDENCY==-1)
                        break;
                    else if(productMap.containsKey(candidate.DEPENDENCY) && productMap.get(candidate.DEPENDENCY)>=candidate.INGREDIENT_COUNT){
                        //jika ternyata bahan yang dibutuhkan ada, hentikan nyari kandidat mesin
                        break;
                    }else{
                        if(machineVariantCount==Factory.getInstance().MAX_RENTABLE_MACHINES_BY_FACTORY && availableSelectedMachinesPair.size() == 1) {
                            //System.out.println("bunuh");
                            state.clear();
                            money = Factory.getInstance().money;
                            machineVariantCount = 0;                    
                            availableSelectedMachinesPair.clear();
                            productMap.clear();
                            avail = new ArrayList<Machine>(Arrays.asList(machinesAvailable));
                            //selectedMachinesPair.clear();
                            continue;
                        }
                        //System.out.println("backtrack akibat produk gak cukup " + candidate.TYPE + " " + machineVariantCount + " " + availableSelectedMachinesPair.size() + " " + state + " " + avail.size());                        
                    }
                }while(true);                                     
            }
                                   
            if(candidate!=null && state.indexOf(candidate.TYPE)==-1) {
                // jika jenis mesin baru, catat mesin tersebut sudah dipilih                                                                  
                availableSelectedMachinesPair.add(new MachineWorkHourPair(candidate, 0));
                productMap.put(candidate.TYPE, 0);
                //selectedMachinesPair.add(availableSelectedMachinesPair.get(availableSelectedMachinesPair.size()-1));
                machineVariantCount++;
                money -= candidate.RENT_COST;                
                if(money < 0) {
                    //System.out.println("bunuh");
                    state.clear();
                    money = Factory.getInstance().money;
                    machineVariantCount = 0;                    
                    availableSelectedMachinesPair.clear();
                    productMap.clear();
                    avail = new ArrayList<Machine>(Arrays.asList(machinesAvailable));
                    //selectedMachinesPair.clear();
                    continue;
                }
            }
                        
            if(candidate != null && money >= 0) {                
                state.add(candidate.TYPE);
                //tambah work hour mesin tersebut
                MachineWorkHourPair mw = searchMachineFromPair(candidate.TYPE, availableSelectedMachinesPair);
                //System.out.println(state.toString() + " " + mw + " " + candidate.TYPE);
                mw.workHour++;    
                Integer count = productMap.get(candidate.TYPE);
                productMap.put(candidate.TYPE, count==null ? candidate.PRODUCTIVITY: count + candidate.PRODUCTIVITY);
                if(candidate.DEPENDENCY != -1) {productMap.put(candidate.DEPENDENCY, productMap.get(candidate.DEPENDENCY) - candidate.INGREDIENT_COUNT);}
                if(mw.workHour == mw.machine.MAX_WORK_HOUR) {
                    //jika jenis mesin ini sudah melebihi jam kerja, buang jenis mesin ini dari pilihan mesin yang bisa dipilih dan sudah terambil
                    availableSelectedMachinesPair.remove(mw);
                    //dan juga buang dari available machine
                    //System.out.println("buang : " + state);
                    avail.remove(candidate);                    
                }   
            }else if(candidate == null) {
                state.add(0);
            }
        }
//        System.out.println("sisa : " + money);        
//        for(int i=0;i<selectedMachinesPair.size();i++) {
//            System.out.println(i + " : " + selectedMachinesPair.get(i).machine.TYPE + " " + selectedMachinesPair.get(i).workHour + " " + selectedMachinesPair.get(i).machine.MAX_WORK_HOUR);
//        }
//        for(Entry<Integer, Integer> entry : productMap.entrySet()) {
//            System.out.println(entry.getKey() + " " + entry.getValue());
//        }
        int[] ret = new int[state.size()];
        Iterator<Integer> iterator = state.iterator();
        for (int i = 0; i < ret.length; i++)
        {
            ret[i] = iterator.next().intValue();
        }
        return ret;        
    }            
    
    //nyari mesin yang gak butuh apa-apa
    private static Machine[] searchIndependentMachine(Machine[] machines) {
        ArrayList<Machine> independentMachines = new ArrayList<Machine>();        
        for(int i=0;i<machines.length;i++) {
            if(machines[i].DEPENDENCY==-1) {
                independentMachines.add(machines[i]);
            }
        }
        Machine[] arr = new Machine[independentMachines.size()];
        return independentMachines.toArray(arr);        
    }          
    
    private static MachineWorkHourPair searchMachineFromPair(int type, ArrayList<MachineWorkHourPair> machinePairs) {
        for(MachineWorkHourPair pair : machinePairs) {
            if(pair.machine.TYPE == type) {
                return pair;
            }
        }
        return null;
    }           
    
    private static class MachineWorkHourPair {
        public Machine machine;
        public int workHour;
        
        public MachineWorkHourPair(Machine amachine, int aworkHour) {
            machine = amachine;
            workHour = aworkHour;
        }
    }
    
    private static void CrossoverMachine (int[] Ind1, int[] Ind2){
        Random rand = new Random();
        int randomResult = 0;
        randomResult = rand.nextInt(Ind1.length);
        int[] temp = new int[Ind1.length];
        for (int i=0; i<temp.length; i++){
            temp[i] = Ind1[i];
        }
        for (int i=randomResult; i<temp.length; i++){
            Ind1[i] = Ind2[i];
            Ind2[i] = temp[i];
        }
    }
    
    private static void Mutate(int[] Ind){
        Random rand = new Random();
        ArrayList<Integer> temp = new ArrayList<Integer>();
        int randomResult = 0;
        for (int i=0; i<Ind.length; i++){
            if (temp.contains(Ind[i])==false){
                temp.add(Ind[i]);
            }
        }
        for (int i=0; i<Ind.length; i++){
            randomResult = rand.nextInt(100);
            if (randomResult<5){
                Ind[i] = temp.get(rand.nextInt(temp.size()));
            }
        }
    }
    public static int FitnessFunction(int[] Arr) {
        ArrayList<Integer> PerluIstirahat = new ArrayList<Integer>();
        ArrayList<Integer> Distance = new ArrayList<Integer>();
        double jarakterpendek = 1000.0;
        int temp = 0;
        int temp2 = 0;
        int last = -1;
        int i = 0;
        int first = 0;
        int total = Factory.getInstance().money;
        ArrayList<Integer> ArrUnique = new ArrayList<Integer>();
        for (i=0; i<Arr.length; i++) {
            if (!ArrUnique.contains(Arr[i])) {
                ArrUnique.add(Arr[i]); //kalo belom ada, tambahin tipe nya
            }
        }
        HashMap<Integer, Integer> Istirahat = new HashMap<Integer, Integer> (ArrUnique.size()*4/3);
        for (i=0; i<ArrUnique.size(); i++) {
            Istirahat.put(ArrUnique.get(i), 0); //inisialisasi, semua mesin defaultnya tidak butuh istirahat
        }
        for (i=0; i<ArrUnique.size(); i++) {
            total = total - Factory.getInstance().getMachineByType(ArrUnique.get(i)).RENT_COST;
        }
        //kurangi setiap ongkos dengan ongkos peminjaman dari mesin
        i = 0;
        HashMap<Integer, Integer> ctr = new HashMap<Integer, Integer> (ArrUnique.size()*4/3); //worst case nya length nya = jumlah mesin yang ada * 4/3
        //karena load factor default = 3/4
        //inisiasi HashMap semuanya dengan 0
        for (i=0; i<ArrUnique.size(); i++) {
            ctr.put(ArrUnique.get(i), 0);
        } //inisiasi semua counter dengan 0
        //System.out.println("counter size = " + ctr.size() );
        
        i = 0;
         first = Arr[0];
        for (int j=0; j<VendorList.getList().size(); j++) {
             if (VendorList.getVendor(j).getAllMachinesForRent().contains(Factory.getInstance().getMachineByType(first))) { //kalo mesinnya ada di vendor itu
                 if (jarakterpendek>Map.posPabrik.distance(VendorList.getVendor(j).getPoint())) {
                     jarakterpendek = Map.posPabrik.distance(VendorList.getVendor(j).getPoint()); //cari jarak terpendek dari pabrik ke yang nyediain mesin yang dicari

                 }
             }
        }
        while (i<Arr.length) {
            PerluIstirahat.trimToSize();
           for (int j=0; j<PerluIstirahat.size(); j++) {
               PerluIstirahat.trimToSize();
               if (Arr[i] != PerluIstirahat.get(j)) { //kalo yang lagi kerja sekarang beda sama yang lagi perlu istirahat
                   temp2 = Istirahat.get(PerluIstirahat.get(j));
                   temp2--;
                   Istirahat.remove(PerluIstirahat.get(j));
                   Istirahat.put(PerluIstirahat.get(j), temp2); //ganti waktu istirahat yang dibutuhkan oleh mesin, ikut berkurang mestinya
                   if (temp2 == 0) {
                        PerluIstirahat.remove(j); //kalo udah 0, apus aja dari ArrayList nya
                        PerluIstirahat.trimToSize();
                   }
               }
           }
         //System.out.println("i = "+i);
         if (last > -1) { //bukan elemen pertama
            if (Arr[i]==last) {
                temp = ctr.get(Arr[i]); //dapetin jumlah utamanya
                temp++;
                if ((temp > Factory.getInstance().getMachineByType(Arr[i]).MAX_CONTINUED_WORK_HOUR) && (Istirahat.get(Arr[i])==0)) {
                    Istirahat.remove(Arr[i]); //remove trus ganti sama nilai waktu istirahat yang dibutuhkan sama mesin itu
                    Istirahat.put(Arr[i], Factory.getInstance().getMachineByType(Arr[i]).REST_TIME);
                    if (!PerluIstirahat.contains(Arr[i])) {
                        PerluIstirahat.add(Arr[i]); //maka perlu istirahat
                    }
                }
                ctr.remove(Arr[i]); //remove dulu, baru nanti masukkin lagi setelah di increment
                ctr.put(Arr[i], temp);
                
             } else {
                last = Arr[i];
                ctr.remove(Arr[i]);
                ctr.put(Arr[i], 0); //reset lagi ke 0
             }
        
        } else {
             last = Arr[i];
        }
         //mencari mesin mana yang memberikan keuntungan terbesar
         if (ctr.get(Arr[i])<=Factory.getInstance().getMachineByType(Arr[i]).MAX_CONTINUED_WORK_HOUR && !PerluIstirahat.contains(Arr[i])) {
             total = total + (Factory.getInstance().getMachineByType(Arr[i]).PRODUCTIVITY * ProductList.getProductByType(Arr[i]).SELLING_PRICE);
             
         } else {
             if (!PerluIstirahat.contains(Arr[i])){
                 PerluIstirahat.add(Arr[i]);
             }
             total = total + (Factory.getInstance().getMachineByType(Arr[i]).COST_OVERLOAD/100 * Factory.getInstance().getMachineByType(Arr[i]).PRODUCTIVITY* ProductList.getProductByType(Arr[i]).SELLING_PRICE);
         }
         //tambahin pake heuristik disini
        
         //cari kumpulan vendor-vendor yang menyediakan mesin pertama ini
         
         i++;

         
        }
        total = total - (int) jarakterpendek * 2000;
        return total;
    }
    
    public static int FitnessSimulate(int[] individu, int StartHour) {
        boolean[] MachineAvailable = new boolean[Factory.getInstance().MAX_MACHINES];
        for (int i = 0; i < MachineAvailable.length; i++) {
            MachineAvailable[i] = false;
        }
        HashMap<Integer, Integer> ProductProduce = new HashMap<Integer, Integer>();
        int Hour = StartHour;
        HashMap<Integer, Integer> RestNeed = new HashMap<Integer, Integer>();
        HashMap<Integer, Integer> ConsecutiveWork = new HashMap<Integer, Integer>();
        HashMap<Integer, Integer> TotalWork = new HashMap<Integer, Integer>();
        int first = 0;
        double jarakterpendek = 1000.0;
        int Minute = 0;
        int MachineTime = 60;
        int CurrentMachine = 0;
        first = individu[0];
        for (int j=0; j<VendorList.getList().size(); j++) {
             if (VendorList.getVendor(j).getAllMachinesForRent().contains(Factory.getInstance().getMachineByType(first))) { //kalo mesinnya ada di vendor itu
                 if (jarakterpendek>Map.posPabrik.distance(VendorList.getVendor(j).getPoint())) {
                     jarakterpendek = Map.posPabrik.distance(VendorList.getVendor(j).getPoint()); //cari jarak terpendek dari pabrik ke yang nyediain mesin yang dicari

                 }
             }
        }
        boolean AbleProduce = true; // indikator mesin selanjutnya bisa bekerja apa tidak
        boolean LastAbleProduce = false; // indikator mesin sebelumnya bisa produksi atau tidak
        
        PathFinding PF = new PathFinding(1);
        PF.process(Factory.getInstance().TRUCK_CAPACITY, individu);        
        
        while (Hour < Factory.getInstance().WORK_HOUR + StartHour) {                       
            
            if(MachineTime==60 && CurrentMachine>0 && MachineAvailable[individu[CurrentMachine-1]-1]) {                
                if (LastAbleProduce == false) {                    
                } else if (ProductProduce.containsKey(individu[CurrentMachine-1]) && LastAbleProduce == true) {
                    Machine m = Factory.getInstance().getMachineByType(individu[CurrentMachine-1]);
                    int increment = ConsecutiveWork.get(individu[CurrentMachine-1]) > m.MAX_CONTINUED_WORK_HOUR ? (int) (m.PRODUCTIVITY*(m.COST_OVERLOAD/100.0)) : m.PRODUCTIVITY;                                         
                    
                    ProductProduce.put(individu[CurrentMachine-1], ProductProduce.get(individu[CurrentMachine-1]) + increment);                    
                } else {
                    Machine m = Factory.getInstance().getMachineByType(individu[CurrentMachine-1]);
                    int increment = ConsecutiveWork.get(individu[CurrentMachine-1]) > m.MAX_CONTINUED_WORK_HOUR ? (int) (m.PRODUCTIVITY*(m.COST_OVERLOAD/100.0)) : m.PRODUCTIVITY;                                         
                    
                    ProductProduce.put(individu[CurrentMachine-1], increment);                    
                }                
            }
            
            boolean machineArrive = false;
            for (int i = 0; i < PF.timeMachine.length; i++) {
                if (PF.timeMachine[i].y==(Hour-StartHour)*60+Minute){
                    if(!machineArrive) {                        
                        machineArrive = true;
                    }
                    MachineAvailable[PF.timeMachine[i].x-1]=true;                    
                }
            }
            
            if(MachineTime == 60) {
                if(TotalWork.containsKey(individu[CurrentMachine]) && TotalWork.get(individu[CurrentMachine])>=Factory.getInstance().getMachineByType(individu[CurrentMachine]).MAX_WORK_HOUR) {
                    //jika mesin tersebut telah bekerja dengan jam kerja melebihi total jam kerja yang diperbolehkan, mesin tersebut gak bisa kerja                    
                    AbleProduce = false;

                } else if (Factory.getInstance().getMachineByType(individu[CurrentMachine]).DEPENDENCY == -1) {                    
                    AbleProduce = true;
                } else if (Hour < Factory.getInstance().WORK_HOUR + StartHour && ProductProduce.containsKey(Factory.getInstance().getMachineByType(individu[CurrentMachine]).DEPENDENCY) && ProductProduce.get(Factory.getInstance().getMachineByType(individu[CurrentMachine]).DEPENDENCY) >= Factory.getInstance().getMachineByType(individu[CurrentMachine]).INGREDIENT_COUNT) {                    
                    AbleProduce = true;
                } else {                                        
                    AbleProduce = false; // mesin berikutnya gak bisa kerja karena kekurangan bahan
                }
            }
            
            if (MachineAvailable[individu[CurrentMachine]-1]) {
                if (MachineTime == 60 && AbleProduce) {                                                            
                    //tepat sebelum bekerja, makan bahan yang dibutuhkan
                    if(Hour < Factory.getInstance().WORK_HOUR + StartHour && ProductProduce.containsKey(Factory.getInstance().getMachineByType(individu[CurrentMachine]).DEPENDENCY) && ProductProduce.get(Factory.getInstance().getMachineByType(individu[CurrentMachine]).DEPENDENCY) >= Factory.getInstance().getMachineByType(individu[CurrentMachine]).INGREDIENT_COUNT) {
                        ProductProduce.put(Factory.getInstance().getMachineByType(individu[CurrentMachine]).DEPENDENCY, (ProductProduce.get(Factory.getInstance().getMachineByType(individu[CurrentMachine]).DEPENDENCY) - Factory.getInstance().getMachineByType(individu[CurrentMachine]).INGREDIENT_COUNT));                        
                    }                    
                                        
                    LastAbleProduce = true;
                    Factory.getInstance().RestAllMachineExcept(RestNeed, Factory.getInstance().getMachineByType(individu[CurrentMachine]));
                    
                    //increment jam kerja berturut2 mesin tersebut
                    if (ConsecutiveWork.containsKey(individu[CurrentMachine])){
                        ConsecutiveWork.put(individu[CurrentMachine], ConsecutiveWork.get(individu[CurrentMachine])+1);
                    } else {
                        ConsecutiveWork.put(individu[CurrentMachine], 1);
                    }
                    
                    //increment jam kerja total mesin tersebut
                    int newTotalWorkVal = TotalWork.containsKey(individu[CurrentMachine]) ? TotalWork.get(individu[CurrentMachine]) + 1 : 1;
                    TotalWork.put(individu[CurrentMachine], newTotalWorkVal);
                    
                    //syarat2 reset jam kerja terus menerus : 
                    // jika jam kerja mesin tersebut belum melebihi beban kerja terus menerus maksimum, reset jam kerja berturut2 mesin tersebut
                    if ((CurrentMachine>0 && ConsecutiveWork.containsKey(individu[CurrentMachine-1]) && ConsecutiveWork.get(individu[CurrentMachine-1]) <= Factory.getInstance().getMachineByType(individu[CurrentMachine-1]).MAX_CONTINUED_WORK_HOUR && individu[CurrentMachine]!=individu[CurrentMachine-1]) 
                            || (CurrentMachine>0 && !ConsecutiveWork.containsKey(individu[CurrentMachine-1]))){                                                                                                
                        ConsecutiveWork.put(individu[CurrentMachine-1], 0);
                    }
                    // cek semua mesin, jika ada yang waktu istirahat nya lebih dari sama dengan REST_TIME maka reset jam kerja berturut2
                    Factory.getInstance().ResetConsWorkIfRestSuff(RestNeed, ConsecutiveWork);
                } else if (MachineTime == 60 && AbleProduce == false) {                    
                    LastAbleProduce = false;
                }
                
                if (MachineTime > 1) {
                    MachineTime--;                    
                }
                else if (MachineTime == 1) {                                        
                    MachineTime = 60;
                    if (CurrentMachine < individu.length - 1) {
                        CurrentMachine++;
                    }                                        
                }
            }
            if (Minute < 59) {
                Minute += 1;
            } else {
                Hour += 1;
                Minute = 0;
            }
        }         
        System.out.println("siml : " + ProductProduce);
        return Factory.getInstance().ProfitCount(MachineAvailable, ProductProduce) - ((int) (jarakterpendek*2000));
    }
    
    public static ArrayList<int[]> CreateIndividuals(int N, int factoryWorkHour, Machine[] machinesAvailable){
        ArrayList<int[]> IndividuList = new ArrayList<int[]>();
        for (int i=0; i<N; i++){
            int[] individu = createRandomIndividu(factoryWorkHour, machinesAvailable);            
            Output.getInstance().println(individu);
            IndividuList.add(individu);
            
            if (IndividuList.size() > 0 && !FactoryPanel.run)
                return IndividuList;
            
        }
        return IndividuList;
    }
    
    public static ArrayList<int[]> SelectIndividu (ArrayList<int[]> IndividuList, Machine[] machinesAvailable){
        ArrayList<int[]> temp = new ArrayList<int[]>();
        for (int i=0; i<IndividuList.size(); i++){
            //int fitnessTest = FitnessFunction(IndividuList.get(i)); 
            int fitnessTest = FitnessSimulate(IndividuList.get(i),7); 
            if(fitnessTest > bestFitness) {
                //simpen individu ini                
                bestFitness = fitnessTest;
                bestIndividu = cloneIndividu(IndividuList.get(i));
                System.out.println("a : " + bestFitness);
                for(int a : bestIndividu) {
                    System.out.print(a + " ");
                }
                System.out.println("");
            }
            
            if (fitnessTest > 0){
                temp.add(IndividuList.get(i));
                Output.getInstance().println(IndividuList.get(i));                      
            }       
            
            if(temp.size()>0 && !FactoryPanel.run)                
                return temp;
        }
        return temp;
    }
    
    public static ArrayList<int[]> CrossoverMutateIndividuList(ArrayList<int[]> IndividuList){
        //kasus khusus , jika individu asalnya cuman 1, gak usah pake crossover(balikin individu itu lagi)
        if(IndividuList.size() == 1) return IndividuList;
        
        ArrayList<int[]> temp = new ArrayList<int[]>();
        int[] Ind1, Ind2;
        for (int i=0; i<IndividuList.size(); i+=2){
            if(IndividuList.size()%2==1 && i==IndividuList.size()-1) break; 
            Ind1 = IndividuList.get(i);
            Ind2 = IndividuList.get(i+1);             
            CrossoverMachine(Ind1, Ind2);
            Mutate(Ind1);
            Mutate(Ind2);
            temp.add(Ind1);
            temp.add(Ind2);            
            Output.getInstance().println(Ind1);
            Output.getInstance().println(Ind2);
            
            if(temp.size()>0 && !FactoryPanel.run)
                return temp;
        }
        return temp;
    }
    
    public static int[] cloneIndividu(int[] individu) {
        int[] ret = new int[individu.length];
        System.arraycopy(individu, 0, ret, 0, individu.length);
        return ret;
    }
    
    public static int[] getFittestIndividu(ArrayList<int[]> individuList) {
        int[] best = individuList.get(0);
        int fitness=FitnessSimulate(best,7);
        for(int[] individu : individuList) {
            int test = FitnessSimulate(individu,7);
            if(fitness < test) {
                best = individu;
                fitness = test;
            }
        }        
        return best;
    }
    
    public static void printIndividu(ArrayList<int[]> IndividuList){
        for (int i=0; i<IndividuList.size(); i++){
            System.out.print("Individu "+i+":");
            for(int a : IndividuList.get(i)) {
                Output.getInstance().print(a + " "); 
            }
            Output.getInstance().println("");
        }
    }
}


